﻿/*
 * Created by SharpDevelop.
 * User: YarUnaderoaker
 * Date: 21.07.2013
 * Time: 22:08
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using System.IO;

using Autodesk;
using Autodesk.Revit;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.UI.Selection;

namespace IronworkDetailedAddIn
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
    public class StartupClass : IExternalApplication
    {
        // ExternalCommands assembly path
        public static string AddInPath = System.Reflection.Assembly.GetExecutingAssembly().Location; //typeof(StartupClass).Assembly.Location;
        // Button icons directory
        static string ButtonIconsFolder = Path.GetDirectoryName(AddInPath);

        public static readonly string MODELWELDING = "Угловой сварочный шов";

        public static UIControlledApplication uiRevit = null;

        public Autodesk.Revit.UI.Result OnStartup(UIControlledApplication ctrlApp)
        {
            try
            {
                // create customer Ribbon Items
                uiRevit = ctrlApp;
                CreateRibbonSamplePanel();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "KMD Ribbon");

                return Autodesk.Revit.UI.Result.Failed;
            }

            return Autodesk.Revit.UI.Result.Succeeded;

        }

        public Autodesk.Revit.UI.Result OnShutdown(UIControlledApplication ctrlApp)
        {
            return Autodesk.Revit.UI.Result.Succeeded;
        }

        private void CreateRibbonSamplePanel()
        {
            // create a Ribbon panel which contains three stackable buttons and one single push button.
            string panelName = "Конструкции металлические детализированые";
            RibbonPanel ribbonPanel = uiRevit.CreateRibbonPanel(panelName);

            PushButtonData pushButtonData1 = new PushButtonData("MakeDetailsShape", "Создать\nдетали", AddInPath, "IronworkDetailedAddIn.MakeDetailsShape");
            PushButton button1 = ribbonPanel.AddItem(pushButtonData1) as PushButton;
            button1.LargeImage = new BitmapImage(new Uri(Path.Combine(ButtonIconsFolder, "MakeDetailsShape.png"), UriKind.Absolute));
            button1.ToolTip = "Создает или обновляет семейства 'Специального оборудования' по форме выбраных элементов несущего каркаса.";

            PushButtonData pushButtonData2 = new PushButtonData("RefreshDetailsShape", "Обновить\nдетали", AddInPath, "IronworkDetailedAddIn.RefreshDetailsShape");
            PushButton button2 = ribbonPanel.AddItem(pushButtonData2) as PushButton;
            button2.LargeImage = new BitmapImage(new Uri(Path.Combine(ButtonIconsFolder, "RefreshDetailsShape.png"), UriKind.Absolute));
            button2.ToolTip = "Обновляет все ранее созданые детали";

            PushButtonData pushButtonData3 = new PushButtonData("FactoryWelding", "Сварка", AddInPath, "IronworkDetailedAddIn.WeldElements");
            PushButton button3 = ribbonPanel.AddItem(pushButtonData3) as PushButton;
            button3.LargeImage = new BitmapImage(new Uri(Path.Combine(ButtonIconsFolder, "WeldElements.png"), UriKind.Absolute));
            button3.ToolTip = "Сваривает выбраные элементы создавая модельные сварные швы";

            PushButtonData pushButtonData5 = new PushButtonData("WeldingAnnotation", "Условные\nобозначения", AddInPath, "IronworkDetailedAddIn.WeldingAnnotation");
            PushButton button5 = ribbonPanel.AddItem(pushButtonData5) as PushButton;
            button5.LargeImage = new BitmapImage(new Uri(Path.Combine(ButtonIconsFolder, "WeldingAnnotation.png"), UriKind.Absolute));
            button5.ToolTip = "Создает или обновляет условные обозначения сварки";

            PushButtonData pushButtonData6 = new PushButtonData("SelectSubcomponents", "Выбор\nсубкомпонентов", AddInPath, "IronworkDetailedAddIn.SelectSubcomponents");
            PushButton button6 = ribbonPanel.AddItem(pushButtonData6) as PushButton;
            button6.LargeImage = new BitmapImage(new Uri(Path.Combine(ButtonIconsFolder, "SelectSubcomponents.png"), UriKind.Absolute));
            button6.ToolTip = "Заменяет в текущем выборе семейства на их вложеные компоненты, сборки на их составляющие, группы на их члены.";
        }

        public static View3D GetStructuralView3D(Document doc)
        {
            // Find a 3D view to use for the ReferenceIntersector constructor
            ElementId phaseId = doc.ActiveView.CreatedPhaseId;
            FilteredElementCollector collector = new FilteredElementCollector(doc);
            Func<View3D, bool> isNotTemplate = v3 => (!v3.IsTemplate &&
                                                        v3.Discipline == ViewDiscipline.Structural &&
                                                        v3.DisplayStyle != DisplayStyle.Wireframe &&
                                                        v3.CreatedPhaseId == phaseId);
            View3D view = collector.OfClass(typeof(View3D)).Cast<View3D>().First<View3D>(isNotTemplate);
            return view;
        }
    }

    public class OverwriteFamilyLoadOptions : IFamilyLoadOptions
    {
        public bool OnFamilyFound(bool familyInUse, out bool overwriteParameterValues)
        {
            overwriteParameterValues = true;
            return true;
        }

        public bool OnSharedFamilyFound(Family sharedFamily, bool familyInUse, out FamilySource source, out bool overwriteParameterValues)
        {
            source = FamilySource.Family;
            overwriteParameterValues = true;
            return true;
        }
    }

    public class Shaper
    {
        private UIApplication app;
        private Document doc;

        public Shaper(UIApplication revit)
        {
            app = revit;
            doc = app.ActiveUIDocument.Document;       
        }

        public bool MakeShapes(Autodesk.Revit.UI.Selection.SelElementSet elementSet)
        {
            bool nothing = true;
            IList<FamilyInstance> notMarked = new List<FamilyInstance>();
            foreach (Element e in elementSet)
            {
                FamilyInstance fi = e as FamilyInstance;
                if (fi != null &&
                    fi.Category.Id.IntegerValue == (int)BuiltInCategory.OST_StructuralFraming)
                {
                    nothing = false;
                    if (!DoCreateOrUpdateDetail(fi)) notMarked.Add(fi);
                }
            }
            if (notMarked.Count > 0)
            {
                elementSet.Clear();
                foreach (FamilyInstance fi in notMarked)
                    elementSet.Add(fi);
                app.ActiveUIDocument.Selection.Elements = elementSet;
                MessageBox.Show("Задайте значение параметру 'Маркировка' для выделеных элементов");
            }
            return nothing;
        }

        public bool MakeShapes(IList<Reference> eRefList)
        {
            bool nothing = true;
            IList<FamilyInstance> notMarked = new List<FamilyInstance>();
            foreach (Reference eRef in eRefList)
            {
                FamilyInstance fi = doc.GetElement(eRef) as FamilyInstance;
                if (fi != null &&
                    fi.Category.Id.IntegerValue == (int)BuiltInCategory.OST_StructuralFraming)
                {
                    nothing = false;
                    if (!DoCreateOrUpdateDetail(fi)) notMarked.Add(fi);
                }
            }
            if (notMarked.Count > 0)
            {
                Autodesk.Revit.UI.Selection.SelElementSet elementSet = app.ActiveUIDocument.Selection.Elements;
                elementSet.Clear();
                foreach (FamilyInstance fi in notMarked)
                    elementSet.Add(fi);
                app.ActiveUIDocument.Selection.Elements = elementSet;
                MessageBox.Show("Задайте значение параметру 'Маркировка' для выделеных элементов");
            }
            return nothing;
        }

        public bool MakeShapes(IList<FamilyInstance> list)
        {
            bool nothing = true;
            IList<FamilyInstance> notMarked = new List<FamilyInstance>();
            foreach (FamilyInstance fi in list)
            {
                if (fi.Category.Id.IntegerValue == (int)BuiltInCategory.OST_StructuralFraming)
                {
                    nothing = false;
                    if (!DoCreateOrUpdateDetail(fi)) notMarked.Add(fi);
                }
            }
            if (notMarked.Count > 0)
            {
                Autodesk.Revit.UI.Selection.SelElementSet elementSet = app.ActiveUIDocument.Selection.Elements;
                elementSet.Clear();
                foreach (FamilyInstance fi in notMarked)
                    elementSet.Add(fi);
                app.ActiveUIDocument.Selection.Elements = elementSet;
                MessageBox.Show("Задайте значение параметру 'Маркировка' для выделеных элементов");
            }

            return nothing;
        }

        public Solid GetSolid(Element e, Options opt)
        {
            Solid solid = null;

            GeometryElement geo = e.get_Geometry(opt);

            if (null != geo)
            {
                Autodesk.Revit.DB.Transform tf = null;
                if (e is FamilyInstance)
                {
                    tf = (e as FamilyInstance).GetTransform();
                    geo = geo.GetTransformed(tf.Inverse);
                }

                GeometryInstance inst = null;
                Autodesk.Revit.DB.Transform t = Autodesk.Revit.DB.Transform.Identity;

                foreach (GeometryObject obj in geo)
                {
                    solid = obj as Solid;

                    if (null != solid
                      && 0 < solid.Faces.Size)
                    {
                        break;
                    }

                    inst = obj as GeometryInstance;
                }

                if (null == solid && null != inst)
                {
                    geo = inst.GetSymbolGeometry();
                    foreach (GeometryObject obj in geo)
                    {
                        solid = obj as Solid;

                        if (null != solid && 0 < solid.Faces.Size)
                        {
                            break;
                        }
                    }
                }
            }
            return solid;
        }

        public String FindSpecialEquipmentTemplate(string familyPath,
                                                          string familyFile,
                                                          string familyMetricFile)
        {
            IEnumerable<string> files = Directory.EnumerateFiles(familyPath, familyFile, SearchOption.AllDirectories);

            String result = files.FirstOrDefault<string>();
            if (!String.IsNullOrEmpty(result))
                return result;

            files = Directory.EnumerateFiles(familyPath, familyMetricFile, SearchOption.AllDirectories);

            return files.FirstOrDefault<string>();
        }

        private IList<Family> GetMetalDetailList()
        {

            List<Family> m_families = new List<Family>();
            ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilySymbol));
            ElementCategoryFilter typeFilter = new ElementCategoryFilter(BuiltInCategory.OST_SpecialityEquipment);
            LogicalAndFilter filter = new LogicalAndFilter(familyFilter, typeFilter);

            FilteredElementIterator elementIterator = (new FilteredElementCollector(doc)).WherePasses(filter).GetElementIterator();
            elementIterator.Reset();

            while (elementIterator.MoveNext())
            {
                object obj = elementIterator.Current;

                FamilySymbol fs = obj as FamilySymbol;

                if (null != fs)
                {
                    Parameter p = fs.get_Parameter(BuiltInParameter.ALL_MODEL_DESCRIPTION);
                    string label = p.AsString();
                    if (string.CompareOrdinal(label, 0, "KMD", 0, 3) == 0)
                    {
                        m_families.Add(fs.Family);
                    }
                }
            }
            return m_families;
        }

        public FamilySymbol GetFirstSymbol(Family family)
        {
            FamilySymbolSetIterator iter = family.Symbols.ForwardIterator();
            iter.Reset();
            iter.MoveNext();
            return iter.Current as FamilySymbol;
        }

        private bool DoCreateOrUpdateDetail(FamilyInstance framing)
        {
            string detailMark = framing.get_Parameter(BuiltInParameter.ALL_MODEL_MARK).AsString();
            if (string.IsNullOrEmpty(detailMark)) return false;

            IList<Family> details = GetMetalDetailList();
            string id = framing.Id.ToString();
            Family detail = null;
            foreach (Family f in details)
            {
                FamilySymbol fs = GetFirstSymbol(f);
                Parameter p = fs.get_Parameter(BuiltInParameter.ALL_MODEL_MANUFACTURER);
                string id_ = p.AsString();
                if (string.Compare(id, id_) == 0)
                {
                    detail = f;
                    break;
                }
            }

            Options opt = app.Application.Create.NewGeometryOptions();
            opt.DetailLevel = doc.ActiveView.DetailLevel;
            Solid solid = GetSolid(framing, opt);
            string familyName;
            string typeName = string.Format("Позиция {0}", detailMark);

            // Создаем новую деталь
            string familyPath = FindSpecialEquipmentTemplate(app.Application.FamilyTemplatePath,
                                                                "Specialty Equipment.rft",
                                                                "Metric Specialty Equipment.rft");
            if (string.IsNullOrEmpty(familyPath))
            {
                familyPath = FindSpecialEquipmentTemplate(app.Application.FamilyTemplatePath,
                                                                "Специализированное оборудование.rft",
                                                                "Метрическая система, специализированное оборудование.rft");
                if (string.IsNullOrEmpty(familyPath))
                    throw new Exception("Невозможно найти шаблон семейства 'Метрическая система, специализированное оборудование.rft' в каталоге шаблонов смейст.");
            }


            // Create family
            Document familyDoc = app.Application.NewFamilyDocument(familyPath);

            if (detail == null)
            {
                // Find unique name
                int i = 0;
                bool unique;
                do
                {
                    unique = true;
                    i++;
                    familyName = string.Format("Деталь {0}", i);
                    foreach (Family f in details)
                    {
                        if (string.Compare(familyName, f.Name) == 0)
                        {
                            unique = false;
                            break;
                        }
                    }

                } while (!unique);
            }
            else
            {
                familyName = detail.Name;
                FamilySymbol fs = GetFirstSymbol(detail);
                if (string.Compare(fs.Name, typeName) != 0)
                {
                    using (Transaction t = new Transaction(doc, string.Format("Переименование детали {0} в {1}", fs.Name, typeName)))
                    {
                        t.Start();
                        fs.Name = typeName;
                        t.Commit();
                    }
                }
            }

            // Create freeform element
            using (Transaction t = new Transaction(familyDoc, string.Format("Создание детали {0}", typeName)))
            {
                t.Start();
                familyDoc.FamilyManager.NewType(typeName);
                Autodesk.Revit.DB.FreeFormElement element = Autodesk.Revit.DB.FreeFormElement.Create(familyDoc, solid);
                t.Commit();
            }

            SaveAsOptions saveOpts = new SaveAsOptions();
            saveOpts.OverwriteExistingFile = true;
            string tempFile = Environment.GetEnvironmentVariable("TEMP//") + familyName;
            familyDoc.SaveAs(tempFile, saveOpts);
            familyDoc.Close(false);
            // Load family into document
            IFamilyLoadOptions familyLoadOptions = new OverwriteFamilyLoadOptions();

            //(delegate(bool familyInUse, out bool overwrite) { overwrite = true; return true; });
            Transaction ta = new Transaction(doc, string.Format("Загрузка детали {0}", typeName));
            ta.Start();
            Family detailFamily;
            if (app.ActiveUIDocument.Document.LoadFamily(tempFile, familyLoadOptions, out detailFamily))
            {
                FamilySymbol fs = GetFirstSymbol(detailFamily);
                Parameter p = fs.get_Parameter(BuiltInParameter.ALL_MODEL_MANUFACTURER);
                string id_ = p.AsString();
                p.Set(framing.Id.ToString());
                p = fs.get_Parameter(BuiltInParameter.ALL_MODEL_DESCRIPTION);
                p.Set("KMD");
                ta.Commit();
            }
            else ta.RollBack();
            return true;
        }        
    }

    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
    public class MakeDetailsShape : IExternalCommand
    {

        public Autodesk.Revit.UI.Result Execute(ExternalCommandData revit,
                                               ref string message,
                                               ElementSet elements)
        {
            UIApplication app = revit.Application;
            Document doc = app.ActiveUIDocument.Document;
            if (doc.IsFamilyDocument) return Autodesk.Revit.UI.Result.Cancelled;
            Shaper shaper = new Shaper(app);

            try
            {
                Autodesk.Revit.UI.Selection.SelElementSet elementSet = app.ActiveUIDocument.Selection.Elements;
                bool nothing = true;
                if (elementSet.Size > 0)
                {
                    nothing = shaper.MakeShapes(elementSet);
                }
                else
                {
                    IList<Reference> eRefList = app.ActiveUIDocument.Selection.PickObjects(Autodesk.Revit.UI.Selection.ObjectType.Element, "Выберите элементы семейства 'Несущий каркас'");
                    nothing = shaper.MakeShapes(eRefList);
                }

                if (nothing) throw new Exception("Выберите элементы семейства 'Несущий каркас'");

            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                return Autodesk.Revit.UI.Result.Failed;
            }

            return Autodesk.Revit.UI.Result.Succeeded;
        }
    }

    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
    public class WeldingAnnotation : IExternalCommand
    {
        const double PRECISION = 0.00001;

        struct WeldLine_Rec
        {
            public XYZ start;
            public XYZ end;
            public double depth; 
            public bool visible;
            public bool behinde;
            public FamilySymbol symbol;
        };

        public Autodesk.Revit.UI.Result Execute(ExternalCommandData revit,
                                               ref string message,
                                               ElementSet elements)
        {
            UIApplication app = revit.Application;
            Document doc = app.ActiveUIDocument.Document;
            Transaction ta = new Transaction(doc, "Обозначение сварки");
            ta.Start();
            
			try
			{
				Autodesk.Revit.DB.View view = doc.ActiveView;
				string scale = "М" + view.Scale.ToString();
				
				FamilySymbol AWsymbol;
				FamilySymbol FWsymbol;
                IList<WeldLine_Rec> lines = new List<WeldLine_Rec>();

				bool received = GetDetailWeldingSymbols(doc, scale, out AWsymbol, out FWsymbol);
				if (received)
				{
                    View3D view3D = StartupClass.GetStructuralView3D(doc);
					if (view3D == null)
						throw new Exception("Для этой операции в проекте должен быть 3D вид категории конструкции, не каркасный и с той же фазой что у активного вида");
					
					DeleteVisibleDetailWelding(doc, view.Id);
					IList<FamilyInstance> list = GetVisibleModelWelding(doc, view.Id);

					Plane plane = new Plane(-view.ViewDirection, view.Origin);
                    double topOffset = 0.0;
                    if (view is ViewPlan)
                    {
                        ViewPlan viewPlan = view as ViewPlan;
                        PlanViewRange viewRange = viewPlan.GetViewRange();
                        topOffset = viewRange.GetOffset(PlanViewPlane.TopClipPlane);
                    }
					ElementFilter filter = GetObstructionFilter();
					double offset = UnitUtils.Convert(3, DisplayUnitType.DUT_MILLIMETERS, DisplayUnitType.DUT_FEET_FRACTIONAL_INCHES);
					foreach(FamilyInstance f in list)
					{
						LocationCurve loc = f.Location as LocationCurve;
						Line line = loc.Curve as Line;
						XYZ n = f.FacingOrientation.CrossProduct(f.HandOrientation);
						XYZ d = line.Direction.Normalize();
						bool reverse;
						double a = view.ViewDirection.AngleTo(n);
						if (a > Math.PI / 4 && a < 3 * Math.PI / 4)
						{
							reverse = (n.CrossProduct(d).DotProduct(view.ViewDirection) < 0.0);
							reverse = reverse ^ f.FacingFlipped;
						}
						else
						{
							reverse = (n.DotProduct(view.ViewDirection) < 0.0);
						}						
						XYZ start = line.GetEndPoint(reverse?1:0);
					    XYZ end = line.GetEndPoint(reverse?0:1);
                        WeldLine_Rec record;
                        record.start = ProjectPointToPlane(plane, start);
                        record.end = ProjectPointToPlane(plane, end);
                        record.depth = signedDistanceTo(plane, start);

                        if (record.start.DistanceTo(record.end) > PRECISION)
					    {
                            string typeName = f.Symbol.Name;
                            if (String.CompareOrdinal(typeName, 0, "Монтажная", 0, 9) == 0)
                            {
                                record.symbol = AWsymbol;
                            }
                            else
                            {
                                record.symbol = FWsymbol;
                            }
					    	
					    	// Проверка стороны размещения шва (с видимой/невидимой)
					    	record.visible = true;
                            record.behinde = false;
					    	ReferenceIntersector refIntersector = new ReferenceIntersector(filter, FindReferenceTarget.Face, view3D);

                            d = (record.end - record.start).Normalize();
                            d = d.CrossProduct(-view.ViewDirection);
                            XYZ center = (record.start + record.end) * 0.5 + new XYZ(0, 0, topOffset) + d * offset;
					    	
        					ReferenceWithContext referenceWithContext = refIntersector.FindNearest(center, -view.ViewDirection);
        					if (referenceWithContext != null)
        					{
	        					Reference reference = referenceWithContext.GetReference();
	        					Element e = doc.GetElement(reference);
                                record.visible = (e.Id == f.Id);
        					}

                            lines.Add(record);
					    }
					    
					}

                    for (int i = 0; i < lines.Count - 1; i++)
                    {
                        WeldLine_Rec rec1 = lines[i];

                        for (int j = i + 1; j < lines.Count; j++)
                        {
                            WeldLine_Rec rec2 = lines[j];
                            if (rec1.start.DistanceTo(rec2.start) < PRECISION &&
                                rec1.end.DistanceTo(rec2.end) < PRECISION)
                            {
                                bool flag = rec1.depth > rec2.depth;
                                rec1.behinde |= flag;
                                rec2.behinde |= !flag;
                            }
                            lines[j] = rec2;
                        }

                        lines[i] = rec1;
                    }

                    foreach (WeldLine_Rec record in lines)
                    {
                        if (!record.behinde)
                        {
                            Line line = Line.CreateBound(record.start, record.end);
                            FamilyInstance welding = doc.Create.NewFamilyInstance(line, record.symbol, view);
                            Parameter p = welding.get_Parameter("Visible");
                            if (p != null) p.Set(record.visible ? 1 : 0);
                        }
                    }
				}
				else throw new Exception("Нет подходящего типа в семействе для масштаба вида");
				
				ta.Commit();
			}
			catch (Exception e)
            {
              MessageBox.Show(e.Message); 	
              ta.RollBack();
              return Autodesk.Revit.UI.Result.Failed;
			}	

            return Autodesk.Revit.UI.Result.Succeeded;
        }

        private static readonly string DETAIL_ASSEMBLY_WELDING = "Обозначение монтажного сварочного шва";
        private static readonly string DETAIL_FACTORY_WELDING = "Обозначение заводского сварочного шва";

        private Family LoadDetailWeldingFamily(Document doc, string familyName)
        {
            IFamilyLoadOptions familyLoadOptions = new OverwriteFamilyLoadOptions();
            Family weldingFamily;
            string familyFile = Path.GetDirectoryName(StartupClass.AddInPath) + "\\" + familyName + ".rfa";
            if (!doc.LoadFamily(familyFile, familyLoadOptions, out weldingFamily)) return null;
            return weldingFamily;
        }

        private bool GetDetailWeldingSymbols(Document doc, string scale, out FamilySymbol AW, out FamilySymbol FW)
        {
            ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilySymbol));
            ElementCategoryFilter catFilter = new ElementCategoryFilter(BuiltInCategory.OST_DetailComponents);
            LogicalAndFilter filter = new LogicalAndFilter(familyFilter, catFilter);

            FilteredElementIterator elementIterator = (new FilteredElementCollector(doc)).WherePasses(filter).GetElementIterator();
            elementIterator.Reset();

            AW = null;
            FW = null;

            // try to find all the doors and windows in the project and add to the list
            while (elementIterator.MoveNext())
            {
                object obj = elementIterator.Current;

                FamilySymbol f = obj as FamilySymbol;

                if (null != f)
                {
                    string familyName = f.Family.Name;
                    string typeName = f.Name;
                    if (string.Compare(typeName, scale) == 0)
                    {
                        if (string.Compare(familyName, DETAIL_ASSEMBLY_WELDING) == 0)
                        {
                            AW = f;
                        }
                        else if (string.Compare(familyName, DETAIL_FACTORY_WELDING) == 0)
                        {
                            FW = f;
                        }
                    }
                }
            }

            if (AW == null)
            {
                Family f = LoadDetailWeldingFamily(doc, DETAIL_ASSEMBLY_WELDING);
                if (f != null)
                {
                    FamilySymbolSet fss = f.Symbols;
                    foreach (FamilySymbol fs in fss)
                    {
                        if (string.Compare(fs.Name, scale) == 0)
                        {
                            AW = fs;
                            break;
                        }
                    }
                }
            }

            if (FW == null)
            {
                Family f = LoadDetailWeldingFamily(doc, DETAIL_FACTORY_WELDING);
                if (f != null)
                {
                    FamilySymbolSet fss = f.Symbols;
                    foreach (FamilySymbol fs in fss)
                    {
                        if (string.Compare(fs.Name, scale) == 0)
                        {
                            FW = fs;
                            break;
                        }
                    }
                }
            }

            return (AW != null && FW != null);
        }
		
		private LogicalAndFilter GetModelWeldingFilter()
		{
            ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilyInstance));
            ElementCategoryFilter catFilter = new ElementCategoryFilter(BuiltInCategory.OST_StructConnections);
            LogicalAndFilter filter = new LogicalAndFilter(familyFilter, catFilter);	
            return filter;
		}
		
		private LogicalAndFilter GetObstructionFilter()
		{
            ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilyInstance));
            ElementCategoryFilter catFilter1 = new ElementCategoryFilter(BuiltInCategory.OST_StructConnections);
            ElementCategoryFilter catFilter2 = new ElementCategoryFilter(BuiltInCategory.OST_StructuralFraming);
            LogicalOrFilter orFilter = new LogicalOrFilter(catFilter1, catFilter2);
            LogicalAndFilter filter = new LogicalAndFilter(familyFilter, orFilter);	
            return filter;
		}
		
		private IList<FamilyInstance> GetVisibleModelWelding(Document doc, ElementId viewID)
		{
			
			IEnumerable<Element> elems = new FilteredElementCollector(doc, viewID).WherePasses(GetModelWeldingFilter()).ToElements();
            
            IList<FamilyInstance> list = new List<FamilyInstance>();
            
            foreach (FamilyInstance inst in elems)
            {            
            	if (string.Compare(inst.Symbol.Family.Name, StartupClass.MODELWELDING) == 0)
            		list.Add(inst);
            }
            
            return list;
		}
		
		private void DeleteVisibleDetailWelding(Document doc, ElementId viewID)
		{
            ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilyInstance));
            ElementCategoryFilter catFilter = new ElementCategoryFilter(BuiltInCategory.OST_DetailComponents);
            LogicalAndFilter filter = new LogicalAndFilter(familyFilter, catFilter);			
            IEnumerable<Element> elems = new FilteredElementCollector(doc, viewID).WherePasses(filter).ToElements();
            
            foreach (FamilyInstance inst in elems)
            {
                if ((string.Compare(inst.Symbol.Family.Name, DETAIL_ASSEMBLY_WELDING) == 0) ||
                    (string.Compare(inst.Symbol.Family.Name, DETAIL_FACTORY_WELDING) == 0))
                    doc.Delete(inst.Id);
            }
		}
		
		private XYZ ProjectPointToPlane(Plane plane, XYZ point)
		{
			double d = signedDistanceTo(plane, point);
			XYZ p = point - d * plane.Normal;
			return p;
		}
		
		private double signedDistanceTo(Plane plane, XYZ point)
		{
			return plane.Normal.DotProduct(point - plane.Origin);
		}
		


    }

    public class Welder
    {
        public Autodesk.Revit.UI.Result Execute(UIApplication app, string modelWeldingType)
        {
            Document doc = app.ActiveUIDocument.Document;
            Transaction ta = new Transaction(doc, "Сварка элементов");
            ta.Start();

            try
            {
                FamilySymbol symbol = GetModelWeldingSymbol(doc, modelWeldingType);
                if (symbol == null)
                    throw new Exception("Не найдено подходящий тип семейства сварного шва");
                View3D view3D = StartupClass.GetStructuralView3D(doc);
                if (view3D == null)
                    throw new Exception("Для этой операции в проекте должен быть 3D вид категории конструкции, не каркасный и с той же фазой что у активного вида");

                Autodesk.Revit.UI.Selection.SelElementSet elementSet = app.ActiveUIDocument.Selection.Elements;

                FamilyInstance framing1 = null;
                FamilyInstance framing2 = null;
                bool noContact = true;
                bool tooShort = false;

                if (elementSet.Size == 2)
                {
                    foreach (Element e in elementSet)
                    {
                        FamilyInstance f = e as FamilyInstance;
                        if (f != null &&
                            f.Category.Id.IntegerValue == (int)BuiltInCategory.OST_StructuralFraming)
                        {
                            if (framing1 == null) framing1 = f;
                            else framing2 = f;
                        }
                    }
                }

                if (framing1 == null || framing2 == null)
                    throw new Exception("Для этой операции необходимо выбрать два элемента семейства 'Несущий каркас'");

                ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilyInstance));
                ElementCategoryFilter catFilter = new ElementCategoryFilter(BuiltInCategory.OST_StructuralFraming);
                LogicalAndFilter filter = new LogicalAndFilter(familyFilter, catFilter);

                Options opt = app.Application.Create.NewGeometryOptions();
                opt.ComputeReferences = true;
                Solid solid1 = GetSolid(framing1, opt, framing1.GetTransform());
                Solid solid2 = GetSolid(framing2, opt, framing2.GetTransform());
                double minLen = UnitUtils.Convert(15, DisplayUnitType.DUT_MILLIMETERS, DisplayUnitType.DUT_FEET_FRACTIONAL_INCHES);
                Autodesk.Revit.UI.Selection.SelElementSet afterSet = Autodesk.Revit.UI.Selection.SelElementSet.Create();

                foreach (Face f1 in solid1.Faces)
                {
                    PlanarFace pf1 = f1 as PlanarFace;
                    if (pf1 == null) continue;

                    foreach (Face f2 in solid2.Faces)
                    {
                        PlanarFace pf2 = f2 as PlanarFace;
                        if (pf2 == null) continue;

                        XYZ n1 = pf1.ComputeNormal(UV.Zero);
                        XYZ n2 = pf2.ComputeNormal(UV.Zero);
                        double a = n1.AngleTo(n2);
                        if (a < 0.349 || a > 2.7925) continue;

                        Curve curve1;
                        if (pf1.Intersect(pf2, out curve1) == FaceIntersectionFaceResult.Intersecting)
                        {
                            Line line1 = curve1 as Line;
                            if (line1 == null) continue;

                            Curve curve2;
                            if (pf2.Intersect(pf1, out curve2) == FaceIntersectionFaceResult.Intersecting)
                            {
                                Line line2 = curve2 as Line;
                                if (line2 == null) continue;

                                XYZ dir = n1.CrossProduct(n2);

                                FamilyInstance baseFraming = framing1;
                                XYZ baseFaceNormal = n1;
                                if (line2.Length < line1.Length)
                                {
                                    line1 = line2;
                                    baseFraming = framing2;
                                    baseFaceNormal = n2;
                                }

                                SolidCurveIntersection isect;
                                isect = solid1.IntersectWithCurve(line1, new SolidCurveIntersectionOptions());
                                if (isect.SegmentCount == 0) continue;
                                isect = solid2.IntersectWithCurve(line1, new SolidCurveIntersectionOptions());
                                if (isect.SegmentCount == 0) continue;

                                XYZ p1 = line1.GetEndPoint(0);
                                XYZ p2 = line1.GetEndPoint(1);
                                XYZ pm = (p1 + p2) * 0.5;

                                double a1 = dir.AngleTo(p2 - p1);
                                if (a1 > 1.57)
                                {
                                    XYZ ex = p2;
                                    p2 = p1;
                                    p1 = ex;
                                }

                                if (line1.Length < minLen)
                                {
                                    tooShort = true;
                                    continue;
                                }

                                ReferenceIntersector refIntersector = new ReferenceIntersector(baseFraming.Id, FindReferenceTarget.Face, view3D);
                                ReferenceWithContext referenceWithContext = refIntersector.FindNearest(pm, -baseFaceNormal);
                                if (referenceWithContext != null)
                                {
                                    Reference target = referenceWithContext.GetReference();
                                    PlanarFace realFace = null;

                                    try
                                    {
                                        realFace = framing1.GetGeometryObjectFromReference(target) as PlanarFace;
                                    }
                                    catch (Autodesk.Revit.Exceptions.ArgumentException ae)
                                    {

                                    }

                                    if (realFace != null)
                                    {
                                        line1 = Line.CreateBound(p1, p2);

                                        try
                                        {
                                            FamilyInstance welding = doc.Create.NewFamilyInstance(target, line1, symbol);
                                            afterSet.Insert(welding);
                                            noContact = false;
                                        }
                                        catch
                                        {

                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                ta.Commit();

                if (noContact)
                {
                    if (tooShort)
                    {
                        MessageBox.Show("Детали соприкасаються, но длинна швов меньше минимальной");
                    }
                    else
                    {
                        MessageBox.Show("Детали несоприкасаються");
                    }
                }
                else app.ActiveUIDocument.Selection.Elements = afterSet;
                    
            }

            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                ta.RollBack();
                return Autodesk.Revit.UI.Result.Failed;
            }

            return Autodesk.Revit.UI.Result.Succeeded;
        }

        private Family LoadModelWeldingFamily(Document doc)
        {
            IFamilyLoadOptions familyLoadOptions = new OverwriteFamilyLoadOptions();
            Family weldingFamily;
            string familyFile = Path.GetDirectoryName(StartupClass.AddInPath) + "\\" + StartupClass.MODELWELDING + ".rfa";
            if (!doc.LoadFamily(familyFile, familyLoadOptions, out weldingFamily)) return null;
            return weldingFamily;
        }

        private FamilySymbol GetModelWeldingSymbol(Document doc, string typeName)
        {
            ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilySymbol));
            ElementCategoryFilter catFilter = new ElementCategoryFilter(BuiltInCategory.OST_StructConnections);
            LogicalAndFilter filter = new LogicalAndFilter(familyFilter, catFilter);

            FilteredElementIterator elementIterator = (new FilteredElementCollector(doc)).WherePasses(filter).GetElementIterator();
            elementIterator.Reset();

            // try to find all the doors and windows in the project and add to the list
            while (elementIterator.MoveNext())
            {
                object obj = elementIterator.Current;

                FamilySymbol f = obj as FamilySymbol;

                if (null != f)
                {
                    string familyName = f.Family.Name;
                    if (string.Compare(familyName, StartupClass.MODELWELDING) == 0 && string.Compare(f.Name, typeName) == 0)
                    {
                        return f;
                    }
                }
            }

            Family family = LoadModelWeldingFamily(doc);
            if (family != null)
            {
                FamilySymbolSet fss = family.Symbols;
                foreach (FamilySymbol fs in fss)
                {
                    string familyName = fs.Family.Name;
                    if (string.Compare(familyName, StartupClass.MODELWELDING) == 0 && string.Compare(fs.Name, typeName) == 0)
                    {
                        return fs;
                    }
                }
            }
            return null;
        }

        public Solid GetSolid(FamilyInstance f, Options opt, Autodesk.Revit.DB.Transform tf)
        {
            Solid solid = null;

            GeometryElement geo = f.get_Geometry(opt);

            if (null != geo)
            {

                GeometryInstance inst = null;

                foreach (GeometryObject obj in geo)
                {
                    solid = obj as Solid;

                    if (null != solid && 0 < solid.Faces.Size)
                    {
                        break;
                    }

                    inst = obj as GeometryInstance;
                }

                if (null == solid && null != inst)
                {
                    geo = inst.GetSymbolGeometry(tf); // inst.Transform

                    foreach (GeometryObject obj in geo)
                    {
                        solid = obj as Solid;

                        if (null != solid && 0 < solid.Faces.Size)
                        {
                            break;
                        }
                    }
                }
            }
            return solid;
        }

    }

    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
    public class WeldElements : Welder, IExternalCommand
    {
        public Autodesk.Revit.UI.Result Execute(ExternalCommandData revit,
                                               ref string message,
                                               ElementSet elements)
        {
            UIApplication app = revit.Application;
            Welder w = new Welder();
            return w.Execute(app, "Заводская - катет 4 мм");
        }
    }

    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
    public class RefreshDetailsShape : IExternalCommand
    {
        public Autodesk.Revit.UI.Result Execute(ExternalCommandData revit,
                                               ref string message,
                                               ElementSet elements)
        {
            UIApplication app = revit.Application;
            Document doc = app.ActiveUIDocument.Document;
            if (doc.IsFamilyDocument) return Autodesk.Revit.UI.Result.Cancelled;

            try
            {
                IList<FamilySymbol> list = GetDetailtShapes(doc);
                IList<FamilySymbol> uselessList = new List<FamilySymbol>();
                IList<FamilyInstance> usenessList = new List<FamilyInstance>();

                foreach(FamilySymbol fs in list)
                {
                    Parameter param = fs.get_Parameter(BuiltInParameter.ALL_MODEL_MANUFACTURER);
                    int id_ = int.Parse(param.AsString());
                    ElementId eID = new ElementId(id_);
                    FamilyInstance elem = doc.GetElement(eID) as FamilyInstance;
                    if (elem != null) usenessList.Add(elem); else uselessList.Add(fs);
                }

                if (usenessList.Count > 0)
                {
                    Shaper shaper = new Shaper(app);
                    shaper.MakeShapes(usenessList);
                }


                if (uselessList.Count > 0)
                {
                    string messageBoxText = "Удалить несвязаные детали?";
                    string caption = "КМД";
                    MessageBoxButtons button = MessageBoxButtons.YesNo;
                    MessageBoxIcon icon = MessageBoxIcon.Warning;
                    DialogResult result = MessageBox.Show(messageBoxText, caption, button, icon);
                    if (result == DialogResult.Yes)
                    {
                        using (Transaction ta0 = new Transaction(doc, "Удаление несвязаных деталей"))
                        {
                            ta0.Start();
                            foreach (FamilySymbol fs in uselessList)
                            {
                                doc.Delete(fs.Family.Id);
                            }
                            ta0.Commit();
                        }
                    }
                }
            }

            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                return Autodesk.Revit.UI.Result.Failed;
            }

            return Autodesk.Revit.UI.Result.Succeeded;
        }

        IList<FamilySymbol> GetDetailtShapes(Document doc)
        {
            ElementClassFilter familyFilter = new ElementClassFilter(typeof(FamilySymbol));
            ElementCategoryFilter catFilter = new ElementCategoryFilter(BuiltInCategory.OST_SpecialityEquipment);
            LogicalAndFilter filter = new LogicalAndFilter(familyFilter, catFilter);

            FilteredElementIterator elementIterator = (new FilteredElementCollector(doc)).WherePasses(filter).GetElementIterator();
            elementIterator.Reset();

            IList<FamilySymbol> list = new List<FamilySymbol>();

            while (elementIterator.MoveNext())
            {
                object obj = elementIterator.Current;

                FamilySymbol fs = obj as FamilySymbol;

                if (null != fs)
                {
                    Parameter p = fs.get_Parameter(BuiltInParameter.ALL_MODEL_DESCRIPTION);
                    string label = p.AsString();
                    if (string.CompareOrdinal(label, 0, "KMD", 0, 3) == 0)
                    {
                        list.Add(fs);
                    }
                }
            }

            return list;
        }
    }

    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
    public class SelectSubcomponents : IExternalCommand
    {

        public Autodesk.Revit.UI.Result Execute(ExternalCommandData revit,
                                               ref string message,
                                               ElementSet elements)
        {
            UIApplication app = revit.Application;
            Document doc = app.ActiveUIDocument.Document;
            Autodesk.Revit.UI.Selection.SelElementSet elementSet = app.ActiveUIDocument.Selection.Elements;
            Autodesk.Revit.UI.Selection.SelElementSet subSet = Autodesk.Revit.UI.Selection.SelElementSet.Create();

            foreach (Element e in elementSet)
            {
                FamilyInstance fi = e as FamilyInstance;
                if (fi != null)
                {
                    ICollection<ElementId> ids = fi.GetSubComponentIds();
                    if (ids.Count > 0)
                    {
                        foreach (ElementId id in ids)
                        {
                            Element sube = doc.GetElement(id);
                            subSet.Add(sube);
                        }
                    }
                    else subSet.Add(e);
                }
                else
                {
                    AssemblyInstance ai = e as AssemblyInstance;
                    if (ai != null)
                    {
                        ICollection<ElementId> ids = ai.GetMemberIds();
                        foreach (ElementId id in ids)
                        {
                            Element sube = doc.GetElement(id);
                            subSet.Add(sube);
                        }
                    }
                    else
                    {
                        Group g = e as Group;
                        if (g != null)
                        {
                            ICollection<ElementId> ids = g.GetMemberIds();
                            foreach (ElementId id in ids)
                            {
                                Element sube = doc.GetElement(id);
                                subSet.Add(sube);
                            }
                        }
                    }
                }

            }
            app.ActiveUIDocument.Selection.Elements = subSet;
            return Autodesk.Revit.UI.Result.Succeeded;
        }
    }
}

